using System;
using System.Data;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Collections;
using System.IO;

using GURUCORE.Framework.Core;
using GURUCORE.Framework.Core.Factory;
using GURUCORE.Framework.DataAccess.DataSource;

namespace GURUCORE.Framework.DataAccess.Provider
{
	[FactoryClass("DataProviders", "DataProvider")]
	public class DataProviderFactory : FactoryBase
	{
		public const string GLOBAL_CACHE_KEY = "DataProviderFactory";

		Hashtable m_htAssemblies;




		public DataProviderFactory(string p_sFileName) : base(p_sFileName, typeof(DataProviderInfo))
		{
			LoadAssemblies(); 
		}

		private void LoadAssemblies()
		{
            int nURIOffset = "file:///".Length;
			
            FileInfo oCurrentAssemblyFile = new FileInfo(Assembly.GetExecutingAssembly().CodeBase.Substring(nURIOffset));
            string sCurrentFolder = oCurrentAssemblyFile.DirectoryName + "\\";
			string sDefaultAsblName = Assembly.GetAssembly(typeof(System.Data.IDbConnection)).ManifestModule.Name;

			m_htAssemblies = new Hashtable();

			ArrayList arrItemName = this.GetItemNames();
			foreach (string sItemName in arrItemName)
			{
				DataProviderInfo oDPInfo = (DataProviderInfo)this.GetItem(sItemName);
				//if is default assembly
				if (oDPInfo.Assembly.Equals(string.Empty) || oDPInfo.Assembly.Equals(sDefaultAsblName))
				{					
					Assembly oAsbl = Assembly.GetAssembly(typeof(System.Data.IDbConnection));
					m_htAssemblies.Add(oDPInfo.Name, oAsbl);					
				}
				else
				{
					//first, find the assembly
					DirectoryInfo oDirInfo;
					oDirInfo = new DirectoryInfo(sCurrentFolder);
                    if (oDirInfo.GetFiles(oDPInfo.Assembly).Length == 0)
                    {
                        string sCLRFolder = string.Empty;
                        try
                        {
                            // sCLRFolder = RuntimeEnvironment.GetRuntimeDirectory();  // {1}
                            sCLRFolder = System.IO.Path.GetDirectoryName(Assembly.GetAssembly(typeof(System.Environment)).Location); // {2} == {1}, but don't affect by AssemblySecurityConfiguration (of .NET security)
                        }
                        catch{}

                        oDirInfo = new DirectoryInfo(sCLRFolder);
                        if (oDirInfo.GetFiles(oDPInfo.Assembly).Length == 0)
                        {
                            throw new FrameworkException(string.Format("Can't load Assembly [{0}] from Location [{1}], file not found or maybe IO security permission", oDPInfo.Assembly, sCLRFolder + oDPInfo.Assembly));
                        }
                        else
                        {
                            oDPInfo.Assembly = sCLRFolder + oDPInfo.Assembly;
                        }
                    }
                    else
                    {
                        oDPInfo.Assembly = sCurrentFolder + oDPInfo.Assembly;
                    }					
					
				    Assembly oAsbl = Assembly.LoadFrom(oDPInfo.Assembly);
				    m_htAssemblies.Add(oDPInfo.Name,oAsbl);    			
				}
			}
		}



        /// <summary>
        /// WRAPPER for GetDbConnection(string p_sConnStr, string p_sSqlDSName)
        /// </summary>
        /// <param name="p_sConnStr"></param>
        /// <returns></returns>
		public IDbConnection GetDbConnection(string p_sConnStr)
		{
			string sDefaultSqlDS = this.GetFactoryAttribute("default");
			return this.GetDbConnection(p_sConnStr,sDefaultSqlDS);
		}

		public IDbConnection GetDbConnection(string p_sConnStr, string p_sSqlDSName)
		{
			DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);

			object[] arrArgs = {p_sConnStr};
			Assembly oAsbl =  (Assembly)m_htAssemblies[p_sSqlDSName];
			try
			{
				Type oType = oAsbl.GetType(oDSInfo.ConnectionClass);
				object oInstance = Activator.CreateInstance(oType,arrArgs);
				return (IDbConnection)oInstance;
			}
			catch (NullReferenceException ex)
			{
				throw ex;
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}


        /// <summary>
        /// WRAPPER for GetDbCommand(IDbConnection p_oConn, string p_sSqlDSName)
        /// </summary>
        /// <param name="p_oConn"></param>
        /// <returns></returns>
		public IDbCommand GetDbCommand(IDbConnection p_oConn)
		{
			string sDefaultSqlDS = this.GetFactoryAttribute("default");
			return this.GetDbCommand(p_oConn,sDefaultSqlDS);
		}

        public IDbCommand GetDbCommand(IDbConnection p_oConn, string p_sSqlDSName)
        {
            DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);

            object[] arrArgs = { string.Empty, p_oConn };
            Assembly oAsbl = (Assembly)m_htAssemblies[p_sSqlDSName];

            Type oType = oAsbl.GetType(oDSInfo.CommandClass);
            object oInstance = Activator.CreateInstance(oType, arrArgs);
            return (IDbCommand)oInstance;
        }







        public string GetProviderInvariantName()
        {
            string sDefaultSqlDS = this.GetFactoryAttribute("default");
            return this.GetProviderInvariantName(sDefaultSqlDS);
        }

        public string GetProviderInvariantName(string p_sSqlDSName)
        {
            DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
            return oDSInfo.ProviderInvariantName;
        }






		public string GetInlineParameterForm()
		{
			string sDefaultSqlDS = this.GetFactoryAttribute("default");
			return this.GetInlineParameterForm(sDefaultSqlDS);
		}

		public string GetInlineParameterForm(string p_sSqlDSName)
		{
			DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
			return oDSInfo.InlineParameterForm;
		}

		public string GetParameterForm()
		{
			string sDefaultSqlDS = this.GetFactoryAttribute("default");
			return this.GetParameterForm(sDefaultSqlDS);
		}

		public string GetParameterForm(string p_sSqlDSName)
		{
			DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
			return oDSInfo.ParameterForm;
		}

		public string GetUnicodeForm()
		{
			string sDefaultSqlDS = this.GetFactoryAttribute("default");
			return this.GetUnicodeForm(sDefaultSqlDS);
		}

		public string GetUnicodeForm(string p_sSqlDSName)
		{
			DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
			return oDSInfo.UnicodeForm;
		}

        public string GetFieldNameWrap(string p_sSqlDSName)
        {
            DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
            return oDSInfo.FieldNameWrap;
        }

		public string GetLatestIdentityStatement()
		{
			string sDefaultSqlDS = this.GetFactoryAttribute("default");
			return this.GetLatestIdentityStatement(sDefaultSqlDS);
		}

		public string GetLatestIdentityStatement(string p_sSqlDSName)
		{
			DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
			return oDSInfo.LatestIdentityStatement;
		}

        public string GetAllowBatchQuery()
        {
            string sDefaultSqlDS = this.GetFactoryAttribute("default");
            return this.GetAllowBatchQuery(sDefaultSqlDS);
        }

        public string GetAllowBatchQuery(string p_sSqlDSName)
        {
            DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
            return oDSInfo.AllowBatchQuery;
        }

        public string GetPagingSelectTemplate()
        {
            string sDefaultSqlDS = this.GetFactoryAttribute("default");
            return this.GetPagingSelectTemplate(sDefaultSqlDS);
        }

        public string GetPagingSelectTemplate(string p_sSqlDSName)
        {
            DataProviderInfo oDSInfo = (DataProviderInfo)this.GetItem(p_sSqlDSName);
            return oDSInfo.PagingSelectTemplate;
        }

        
    }
}
